#include "StaticMesh.h"

#include "Object/NewObject.h"

#include "Math/MathBase.h"
#include "Render/RenderContext.h"
#include "Render/Mesh/RenderMesh.h"

#include "Asset/EngineDefaultPackage.h"


TSharedObjectPtr<RStaticMesh> RStaticMesh::CreateMesh(
    const TVector<SVertex>& InVertice,
    const TVector<uint32_t>& InIndices)
{
    
    auto Mesh=NewObject<RStaticMesh>();
    Mesh->SetVertice(InVertice,InIndices,true);
    Mesh->DefaultMaterials.Add(REngineDefaultPackage::Get()->GetDefaultMaterial());
    Mesh->SyncToRenderThread();
    return Mesh;
}

TSharedObjectPtr<RStaticMesh> RStaticMesh::CreateMesh(
    const TVector<SVertexGroup>& InVertexGroup)
{
    assert(!InVertexGroup.Empty());
    auto Mesh=NewObject<RStaticMesh>();
    Mesh->SetVertice(InVertexGroup,true);

    Mesh->DefaultMaterials.Reserve(InVertexGroup.Num());
    for(size_t i=0;i<InVertexGroup.Num();i++)
    {
        Mesh->DefaultMaterials.Add(REngineDefaultPackage::Get()->GetDefaultMaterial());
    }

    Mesh->SyncToRenderThread();
    return Mesh; 

}

TVector<std::shared_ptr<CRenderMesh>> RStaticMesh::GetRenderMeshes()
{
    return RenderMeshes;
}

void RStaticMesh::PostLoadFromAsset()
{
    SyncToRenderThread();
}

void RStaticMesh::SyncToRenderThread()
{
    assert(!VertexGroups.Empty());
    //remove extra render mesh or create new render mesh
    if(RenderMeshes.Num()>VertexGroups.Num())
    {
        RenderMeshes.Resize(VertexGroups.Num());
    }
    else if(RenderMeshes.Num()<VertexGroups.Num())
    {
        for(size_t i=RenderMeshes.Num();i<VertexGroups.Num();i++)
        {
            RenderMeshes.Add(std::make_shared<CRenderMesh>());
        }
    }

    //update render mesh data
    for(size_t i=0;i<VertexGroups.Num();i++)
    {
        RenderMeshes[i]->SetMeshData(VertexGroups[i].Vertice,VertexGroups[i].Indices);
    }

}



RStaticMesh::RStaticMesh()
{
}

void RStaticMesh::SetBoundingBox(SBoundingBox InBoundingBox)
{
    Boundary=InBoundingBox;
    BoundingSphere=CMathBase::Max(Boundary.Min.GetLength(),Boundary.Max.GetLength());
}



void RStaticMesh::SetVertice(const TVector<SVertex>& InVertice
        ,const TVector<uint32_t>& InIndices
        ,bool UpdateBounding)
{
    VertexGroups.Add(SVertexGroup(InVertice,InIndices));
    //Vertice=InVertice;
    if(!UpdateBounding)
    {
        return;
    }


    if(!InVertice.Empty())
    {
        SVec3 Min=InVertice[0].Position;
        SVec3 Max=InVertice[0].Position;

        for(auto& Vertex: InVertice)
        {
            Max.X=CMathBase::Max(Vertex.X(),Max.X);
            Max.Y=CMathBase::Max(Vertex.Y(),Max.Y);
            Max.Z=CMathBase::Max(Vertex.Z(),Max.Z); 

            Min.X=CMathBase::Min(Vertex.X(),Min.X);
            Min.Y=CMathBase::Min(Vertex.Y(),Min.Y);
            Min.Z=CMathBase::Min(Vertex.Z(),Min.Z);                   
        }
        Boundary=SBoundingBox(Min,Max);
    }
    else
    {
        Boundary=SBoundingBox(SVec3(0,0,0),SVec3(0,0,0));
    }

    BoundingSphere=CMathBase::Max(Boundary.Min.GetLength(),Boundary.Max.GetLength());

}

void RStaticMesh::SetVertice(TVector<SVertexGroup> InVertexGroups, bool UpdateBounding)
{
    VertexGroups=InVertexGroups;
    //Vertice=InVertice;
    if(!UpdateBounding)
    {
        return;
    }

    Boundary=SBoundingBox(SVec3(0,0,0),SVec3(0,0,0));
    SVec3 Min=SVec3(std::numeric_limits<Float>::max(),std::numeric_limits<Float>::max(),std::numeric_limits<Float>::max());
    SVec3 Max=SVec3(-std::numeric_limits<Float>::max(),-std::numeric_limits<Float>::max(),-std::numeric_limits<Float>::max());
    for(auto& Group: VertexGroups)
    {
        for(auto& Vertex:Group.Vertice)
        {
            
            Max.X=CMathBase::Max(Vertex.X(),Max.X);
            Max.Y=CMathBase::Max(Vertex.Y(),Max.Y);
            Max.Z=CMathBase::Max(Vertex.Z(),Max.Z); 

            Min.X=CMathBase::Min(Vertex.X(),Min.X);
            Min.Y=CMathBase::Min(Vertex.Y(),Min.Y);
            Min.Z=CMathBase::Min(Vertex.Z(),Min.Z);                   
        
            Boundary=SBoundingBox(Min,Max);
        
        }
    }

    BoundingSphere=CMathBase::Max(Boundary.Min.GetLength(),Boundary.Max.GetLength());    

}

int32_t RStaticMesh::GetSectionNum()
{
    return VertexGroups.Num();
}
